package com.zsh.myserver.core;

import com.zsh.myserver.core.configuration.ExceptionCodeConfiguration;
import com.zsh.myserver.exception.HttpException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;

/**
 * Copyright (C), 2020-2020
 * Author: Zhoushaohua
 * Date: 2020/4/11 15:48
 * Description: 全局异常通知(加强)
 */
@ControllerAdvice
@ResponseBody
public class GlobalExceptionAdvice {
    //该类被打上了@Component，可以被自动注入
    @Autowired
    private ExceptionCodeConfiguration codeConfiguration;

    /**
     * 抛出未知异常
     * 对于前端开发者和用户都是无意义的。此种异常来自于服务器开发者的代码逻辑产生的异常。
     *
     * @param request
     * @param e
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)//状态码修改
    public UnifyResponse handleException(HttpServletRequest request, Exception e) {
        System.out.println("handle Exception.");
        UnifyResponse response = new UnifyResponse(9999, "服务器异常", "tmp url");
        //需要把该response返回到前端
        return response;
    }

    /**
     * 处理已知异常，自义定异常。
     * 对于前端开发者和用户有意义，并且需要了解，对用户体验有影响。
     *
     * @param request
     * @param e
     */
    @ExceptionHandler(value = HttpException.class)
    public ResponseEntity<UnifyResponse> handleHttpException(HttpServletRequest request, HttpException e) {
        System.out.println("handle Http Exception.");
        UnifyResponse response = new UnifyResponse(
                e.getCode(),
                codeConfiguration.getExceptionMessage(e.getCode()),
                request.getMethod() + " " + request.getRequestURI());
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());

        ResponseEntity<UnifyResponse> responseEntity = new ResponseEntity<>(response, headers, httpStatus);

        return responseEntity;
    }

    /**
     * 处理校验异常
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)//400，参数错误
    public UnifyResponse handleBeanValidationException(HttpServletRequest request,
                                                       MethodArgumentNotValidException e) {
        String uri = request.getRequestURI();
        String method = request.getMethod();

        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        String message = formatAllErrorMessages(errors);
        return new UnifyResponse(
                10001,
                message,
                method + " " + uri);
    }

    /**
     * 处理URL路径中的参数错误；形容BannerController#testGet3方法调用
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)//400，参数错误
    public UnifyResponse handleConstrainException(HttpServletRequest request, ConstraintViolationException e) {
        String uri = request.getRequestURI();
        String method = request.getMethod();
        StringBuilder msg = new StringBuilder();
        for (ConstraintViolation error : e.getConstraintViolations()) {
            msg.append(error.getMessage()).append(";");
        }
        return new UnifyResponse(
                10001,
                msg.toString(),
                method + " " + uri);
    }

    /**
     * 拼接所有错误信息
     *
     * @param errors 错误消息列表
     * @return 拼接好的错误消息
     */
    private String formatAllErrorMessages(List<ObjectError> errors) {
        StringBuilder msg = new StringBuilder();
        errors.forEach(error -> msg.append(error.getDefaultMessage()).append(";"));
        return msg.toString();
    }

}
